Skip to content

Migrate conversation language field default to 'en' - #11349

Open
undivisible wants to merge 1 commit into
mainfrom
fix/migrate-db-language-field-10040660832344661720
Open

Migrate conversation language field default to 'en'#11349
undivisible wants to merge 1 commit into
mainfrom
fix/migrate-db-language-field-10040660832344661720

Conversation

@undivisible

@undivisible undivisible commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Migrate conversation language field default to 'en'

Defaults Conversation, CreateConversation, and ExternalIntegrationCreateConversation language to 'en', preserving explicit non-English values. The sync pipeline threads the detected/preferred language into CreateConversation. Adds a bounded, resumable backfill migration (backend/migrations/008_migrate_language_to_en.py) that pages users, supports --max-users/--max-writes/--start-after/--dry-run/--workers, and exits non-zero on per-user failures.

Tests: 81 passed.

Line-Count-Exception: backend/utils/sync/pipeline.py | 2499 -> 2500 | threads the resolved detected language into CreateConversation so sync conversations persist the actual language instead of the 'en' default

@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5bc4e1a2-b781-4698-89cf-a72835462ac6)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3204027c2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# Initialize Firebase Admin SDK
try:
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Defer Firebase initialization until migration execution

Move Firebase initialization and firestore.client() into main() (and inject the client into the helpers). The required python backend/scripts/scan_import_time_side_effects.py check reports this new top-level firebase_admin.initialize_app call, so the commit cannot pass preflight; it also prevents importing the migration helpers without initializing credentialed infrastructure.

AGENTS.md reference: backend/AGENTS.md:L235-L235

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: Firebase init and client construction are fully deferred; the migration calls database._client.get_firestore_client() only inside execution paths, and scan_import_time_side_effects.py reports 0 violations.

Comment on lines +93 to +94
except Exception as e:
logger.error(f"Error processing {uid}: {e}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail the migration when any user update fails

If a Firestore read or batch commit fails for even one user, this handler only logs the exception and the process still prints Done and exits successfully. A transient failure or concurrent document deletion can therefore leave part of the database unmigrated while deployment automation or an operator records the migration as successful; track failures and return a nonzero exit status after all futures finish.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: worker failures are counted and the process exits non-zero (sys.exit(1)), so automation cannot record a partial run as successful. Covered by test_worker_failure_exits_nonzero.


source: Optional[ConversationSource] = ConversationSource.omi
language: Optional[str] = None # applies only to Friend # TODO: once released migrate db to default 'en'
language: Optional[str] = 'en' # applies only to Friend

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add regression coverage for the new language defaults

Add tests proving that omitted language values default to en for all three changed conversation models, and that the migration updates missing/empty values while preserving existing languages and exposing write failures. This commit changes production defaults and adds a data migration without modifying any test, contrary to the repository's required behavior-change coverage.

AGENTS.md reference: AGENTS.md:L28-L28

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: test_conversation_language_default.py proves omitted language defaults to 'en' on all three models with explicit values preserved; test_migrate_language_to_en.py covers missing/empty backfill, existing-language preservation, dry-run, and failure exit.


source: ConversationSource = ConversationSource.omi
language: Optional[str] = None
language: Optional[str] = 'en'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the detected language when creating sync conversations

The sync pipeline derives language from the user's explicit preference or STT detection, but constructs CreateConversation without passing it and supplies it only as the separate processing argument (utils/sync/pipeline.py:1075,1149-1162). With this new default, _get_conversation_obj serializes such Spanish or other non-English conversations as language='en'; subsequent reprocessing uses the persisted conversation.language, so summaries and derived data can be regenerated in English. Pass the resolved language into the model rather than allowing the default to replace a known value.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 02fb397: process_segment now passes the resolved language into CreateConversation, with regression test test_detected_language_is_stored_on_new_conversation (French-detected sync persists 'fr').

# so we get all conversations and filter locally, or just check 'language' field.
# To be efficient, we'll stream conversations and update those needing it.

conversations = list(conversations_ref.stream())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stream projected conversation fields during the backfill

For users with long histories or large encrypted transcript documents, list(conversations_ref.stream()) downloads every field and retains the user's entire conversation history in memory; with 64 users processed concurrently, the production-wide migration can exhaust memory and transfer far more Firestore data than needed before it reaches the updates. Iterate the stream directly and project only language so memory remains bounded and transcript blobs are not fetched.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: conversations are streamed with .select(['language']) only, no list() materialization; users are paged by document id so memory stays bounded.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Confidence score: 3/5

  • In backend/models/conversation.py, defaulting CreateConversation.language to 'en' creates a concrete regression risk for callers that omit the field (such as the sync pipeline), which can silently store conversations with the wrong language and affect downstream language-dependent behavior—ensure detected/preferred language is always passed explicitly or remove the default and fail fast when missing.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/models/conversation.py">

<violation number="1" location="backend/models/conversation.py:289">
P1: With `CreateConversation.language` now defaulting to `'en'`, any caller that constructs `CreateConversation` without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist `language='en'` even for non-English conversations. Since `conversation.language` is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic


source: ConversationSource = ConversationSource.omi
language: Optional[str] = None
language: Optional[str] = 'en'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: With CreateConversation.language now defaulting to 'en', any caller that constructs CreateConversation without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist language='en' even for non-English conversations. Since conversation.language is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/models/conversation.py, line 289:

<comment>With `CreateConversation.language` now defaulting to `'en'`, any caller that constructs `CreateConversation` without explicitly passing the detected/preferred language (e.g. the sync pipeline, which resolves language separately) will silently persist `language='en'` even for non-English conversations. Since `conversation.language` is used for later reprocessing, this can cause summaries and derived data to be regenerated in English for non-English conversations. Ensure all callers pass the resolved language into the model instead of relying on the new default.</comment>

<file context>
@@ -286,7 +286,7 @@ class CreateConversation(BaseModel):
 
     source: ConversationSource = ConversationSource.omi
-    language: Optional[str] = None
+    language: Optional[str] = 'en'
 
     processing_conversation_id: Optional[str] = None
</file context>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered by the fix for 3746820357 (02fb397): the sync pipeline threads the detected/preferred language into CreateConversation and a non-English sync regression test asserts it.

Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
Comment thread backend/migrations/008_migrate_language_to_en.py Outdated
@mintlify

mintlify Bot commented Aug 10, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
omi 🟢 Ready View Preview Aug 10, 2026, 6:04 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@undivisible undivisible added human Human-authored pull request backend Backend Task (python) privacy-review Touches user-data persistence, permissions, or privacy-sensitive surfaces AI and removed human Human-authored pull request labels Aug 10, 2026
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_065eca9c-02b5-4d79-8b24-9ecf232bdd3d)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3f793ddc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +21 to +22
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the shared Firestore client for migration credentials

When an operator uses the backend-supported SERVICE_ACCOUNT_JSON credential mode, credentials.ApplicationDefault() ignores that variable and the migration exits because it only accepts ADC via GOOGLE_APPLICATION_CREDENTIALS. Use database._client.get_firestore_client() so the migration honors the repository's credential preparation and configured Firestore target instead of requiring operators to translate supported credentials manually.

AGENTS.md reference: backend/AGENTS.md:L191-L193

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: the migration uses database._client.get_firestore_client(), which routes through prepare_google_credentials and honors SERVICE_ACCOUNT_JSON.

@undivisible undivisible added needs-maintainer-review Needs a human maintainer to sign off before merge needs-tests PR introduces logic that should be covered by tests labels Aug 10, 2026

@undivisible undivisible left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the default language to en can silently rewrite non-English synced conversations because affected creation paths do not propagate detected language. Please preserve detected language for synced conversations, constrain the default to the intended Friend migration, and add regression coverage for a non-English sync.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_6603c84c-ab69-4c78-a529-414bf1e453fd)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1794dae771

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


for doc in conversations:
data = doc.to_dict()
if data.get('source') not in ('friend', 'friend_com'):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Backfill Omi-source conversations too

Historical sync-created conversations are stored with source omi: process_segment defaults its source to ConversationSource.omi, and before this commit its CreateConversation call omitted the language. This filter therefore skips exactly those existing records that can lack the field, leaving their persisted language unset even though the migration reports success. Include omi in the backfill scope rather than limiting it to the legacy Friend source names.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: the migration applies to all conversation sources (no source filter), so omi-source sync conversations with missing language are backfilled too.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf7b0f620a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"backend/utils/retrieval/tools/calendar_tools.py": 1513,
"backend/utils/stt/streaming.py": 1523,
"backend/utils/sync/pipeline.py": 2499
"backend/utils/sync/pipeline.py": 2500

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the ratchet to the unchanged source count

Reset this entry to 2499 and remove the unrelated baseline raise. backend/utils/sync/pipeline.py is not modified by this commit and remains 2499 lines, so running check_product_file_line_count_ratchet.py against the commit reports both that the source file is absent from the diff and that the raised limit does not match its line count; this makes the required preflight contract fail.

AGENTS.md reference: AGENTS.md:L34-L35

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The baseline was already restored to 2499 at eff8ac8; it is now raised to 2500 with a justification only because 02fb397 adds the language=language line to pipeline.py, and the ratchet check passes.

@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_207e2a77-843a-4f14-8794-b66e7da92bf6)

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this through — the backend/model default itself looks consistent, but I’m going to block until the migration/CI pieces are made safe and green.

Blocking items:

  1. backend/migrations/008_migrate_language_to_en.py is a production Firestore data migration, but it eagerly loads every user (get_all_users() returns list(users_ref.stream())) and then eagerly loads every conversation for each user (conversations = list(conversations_ref.stream())) while running 64 users in parallel. For Omi-scale user data this has a high blast radius: unbounded memory/read load, no paging/checkpoint/resume, and no operator throttle beyond --dry-run. Please make this migration bounded and operationally safe before merge (stream or page users/conversations, lower/configure concurrency, include progress/checkpoint/resume or at least an explicit max-users/max-writes guard, and document the dry-run/rollout command).

  2. CI is currently red on this head. Dart Analyze & Tests fails the analyzer ratchet with override_on_non_overriding_member: 5 found, baseline 0, while Hygiene/PR Metadata Preflight fail failure-class-protocol because the fix: commits do not declare Failure-Class: FC-<slug> | new | none. Please fix the ratchet/generated-file state and the required failure-class declaration so the PR is mergeable.

File-specific notes from this review:

  • backend/models/conversation.py: the three Pydantic defaults now make omitted conversation language resolve to en; that matches the stated backend/API intent, but it makes the migration/rollout correctness more important because omitted values will no longer remain distinguishable from English.
  • app/lib/backend/schema/gen/conversation_wire.g.dart: the generated client parser now also defaults missing language to "en", which is consistent with the backend but should be regenerated/validated with the app analyzer green.
  • backend/migrations/008_migrate_language_to_en.py: needs the production-safety changes above before it should be run against user conversation data.
  • docs/api-reference/app-client-openapi.json: the two added OpenAPI default: "en" entries match the backend request/response schema change.
  • docs/api-reference/integration-public-openapi.json: the public integration schema also reflects the new default; this is worth keeping because external clients may rely on the documented default.
  • app/analysis_baseline.json: the baseline edit does not match the current analyzer result, so it needs to be corrected together with the generated app changes.

Leaving this as changes requested because it touches user-data persistence and the current migration path is too broad to run safely as-is; maintainer sign-off is also needed for the data backfill rollout once the script is bounded.


Reviewed by Omi's automated maintainer review (glm-5.2).


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 926de2a to 83a8a7c Compare August 10, 2026 21:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_55b3e555-ceeb-40ae-962e-95b17460df2d)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83a8a7cfe6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


elapsed = time.time() - start
logger.info(f"Done in {elapsed:.1f}s")
logger.info(f"Results: {total_updates} conversations updated across {users_updated} users.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report dry-run results as prospective updates

When the migration is invoked with --dry-run, process_user_conversations increments both counters without committing any writes, but this final message still reports that the conversations were "updated." An operator relying on the preview output as rollout evidence could therefore mistake a dry run for a completed migration; label these as conversations that would be updated when args.dry_run is true.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 8a6c13d: dry-run output reports 'would be updated' instead of 'updated'; asserted in test_dry_run_reports_without_committing.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. I rechecked the new head and the generated/model default changes are aligned, but I still need to keep this blocked because the production backfill script is not safe enough to run against user conversation data yet, and the branch is still red.

Blocking items:

  1. backend/migrations/008_migrate_language_to_en.py still loads the full users collection into memory (get_all_users() returns list(users_ref.stream()) at lines 36-39), then loads every conversation for each user into memory (conversations = list(conversations_ref.stream()) at line 52), and runs 64 users in parallel (line 92). That addresses the intended backfill, but it still has unbounded read/memory/write blast radius and no checkpoint/resume, paging, max-user/max-write guard, or operator throttle beyond --dry-run. Please make the migration bounded and resumable/throttled before this can be run on production user data.

  2. CI is still failing on this head (Hygiene, and the current check rollup also shows a failing PR Metadata Preflight). Please get the required hygiene/preflight state green before merge.

File-specific review notes:

  • backend/models/conversation.py: the Conversation, CreateConversation, and ExternalIntegrationCreateConversation defaults now all resolve omitted language to en. That is consistent with the PR goal, but it changes the meaning of omitted language, so the data rollout and any non-English import/sync paths need maintainer confidence.
  • app/lib/backend/schema/gen/conversation_wire.g.dart: the generated Dart constructor and fromJson reader now default missing language to "en", matching the backend schema; this looks mechanically consistent with the model change.
  • backend/migrations/008_migrate_language_to_en.py: the migration still filters missing/empty language values correctly, but it needs bounded streaming/paging and operational controls before it is safe for a production Firestore backfill.
  • docs/api-reference/app-client-openapi.json: the two app-client OpenAPI schema entries now document default: "en", matching the backend request/response defaults.
  • docs/api-reference/integration-public-openapi.json: the public integration schema now documents the same default: "en"; because external clients may rely on this contract, it should stay in sync with the backend behavior.
  • app/analysis_baseline.json: the prefer_final_fields ratchet is reduced from 4 to 3, which is a positive cleanup, but CI still needs to be green before merge.

I also checked the prior automated blocking review: the Dart analyzer portion appears addressed, but the Firestore migration safety concern is still present on the current head, so I’m not dismissing the earlier changes-requested review. Leaving this for human maintainer sign-off because this is a user-data backfill/default-semantics change, not just a mechanical generated-schema update.


Reviewed by Omi's automated maintainer review (glm-5.2).


by AI on behalf of David — for maintainer follow-up, please @Git-on-my-level with the specific rollout or data-safety question.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 83a8a7c to eff8ac8 Compare August 10, 2026 23:11
@cursor

cursor Bot commented Aug 10, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5326ca3b-ca26-435b-a913-f65851403c37)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02fb397539

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

while not max_users or collected < max_users:
query = db.collection('users').order_by('__name__').limit(page_size)
if last_id:
query = query.start_after([last_id])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass a document reference to the pagination cursor

When the full migration reaches its second page, or whenever --start-after is supplied, this passes a raw string as the cursor value for order_by('__name__'). Firestore document-ID cursors require a document snapshot/reference (the repository's other paginated queries pass the last snapshot or collection.document(id)), so the production query rejects this cursor even though the fake test accepts it; the migration therefore cannot progress beyond the first page. Use the last snapshot or a document reference in the cursor.

Useful? React with 👍 / 👎.

Comment on lines +135 to +139
for uid in iter_user_ids(db, start_after=args.start_after, page_size=args.page_size, max_users=args.max_users):
if args.max_writes and total_writes >= args.max_writes:
logger.info('Reached --max-writes %d, stopping', args.max_writes)
break
futures[executor.submit(process_user_conversations, db, uid, args.dry_run)] = uid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce --max-writes while work is running

For every invocation with --max-writes, total_writes remains zero throughout this entire scheduling loop because results are not collected until the subsequent as_completed loop. Consequently the limit never stops scheduling, and a staged run such as --max-writes 1000 can update the whole database. Results need to be consumed while scheduling, with the remaining write budget enforced before additional user work is allowed.

Useful? React with 👍 / 👎.

Comment on lines +158 to +161
'Finished in %.1fs with %d failed user(s); rerun with --start-after %s to resume',
elapsed,
failures,
last_uid,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not resume after an out-of-order completion

When any worker fails, last_uid is merely the most recently completed successful future, not the greatest contiguous user ID processed before the failure. For example, if u1 fails and u2 completes afterward, the suggested --start-after u2 retry permanently skips u1 while the idempotent migration appears complete. Track a contiguous ordered checkpoint, explicitly retry failed IDs, or advise rerunning without this cursor.

Useful? React with 👍 / 👎.

if args.max_writes and total_writes >= args.max_writes:
logger.info('Reached --max-writes %d, stopping', args.max_writes)
break
futures[executor.submit(process_user_conversations, db, uid, args.dry_run)] = uid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound the number of submitted user futures

On the default unlimited full run, this submits one future for every user before collecting any result, and both the executor's unbounded queue and futures retain all of them. The user iterator's paged reads and eight-worker setting therefore do not bound memory; on the repository's 100K-plus user collection, the migration can consume substantial memory or be killed before processing finishes. Keep only a small bounded window of futures in flight and replenish it as each completes.

Useful? React with 👍 / 👎.

@Git-on-my-level Git-on-my-level removed the needs-tests PR introduces logic that should be covered by tests label Aug 12, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update — this resolves the blockers I had on the earlier heads. I’m leaving this as a positive maintainer signal rather than a formal approval because it still changes a production user-data migration path and should get maintainer sign-off on rollout before merge.

What I checked:

  • backend/migrations/008_migrate_language_to_en.py now pages users with iter_user_ids(), lowers/configures concurrency through --workers, supports --max-users, --max-writes, --start-after, and --dry-run, and exits non-zero on per-user failures. That addresses the earlier unbounded Firestore sweep / partial-success concern.
  • backend/models/conversation.py consistently defaults Conversation, CreateConversation, and ExternalIntegrationCreateConversation language to 'en' while still preserving explicitly provided non-English values.
  • backend/utils/sync/pipeline.py now passes the detected/preferred language into CreateConversation, so the new default should not silently overwrite non-English sync conversations.
  • backend/tests/unit/test_sync_transcription_prefs.py adds the important regression case for detected 'fr' being persisted on newly synced conversations.
  • backend/tests/unit/test_migrate_language_to_en.py covers missing/empty backfill behavior, existing-language preservation, dry-run behavior, pagination/resume, max-user bounds, and failure propagation.
  • backend/tests/unit/test_conversation_language_default.py covers the model default and explicit-language preservation paths.
  • app/lib/backend/schema/gen/conversation_wire.g.dart and the two OpenAPI JSON files mirror the new 'en' default for clients/docs.
  • The ratchet/baseline files (.github/scripts/product_file_line_count_ratchet_baseline/backend-utils.json, app/analysis_baseline.json) are consistent with the implementation/test churn.

CI is green in the context I reviewed. I’m also dismissing the stale automation change-request reviews from older heads because their specific blockers — unbounded migration shape, failure handling, and red checks — are resolved on this head.

Remaining maintainer note: because this backfills persisted conversation data, please run the migration in a staged rollout (--dry-run, then bounded --max-users / --max-writes, resume from logged user IDs) and confirm that defaulting missing/empty historical language to English is the intended product/data policy before full execution.

Automated maintainer review by glm-5.2 for Omi.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level
Git-on-my-level dismissed stale reviews from themself August 12, 2026 09:51

Resolved on current head: migration is now bounded/resumable, failures exit non-zero, sync language is preserved, and checks are green.

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_3b5c7273-3466-42a8-a562-b85185286941)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e72b0bb841

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

uid = next(uid_iter)
except StopIteration:
return
pending[executor.submit(process_user_conversations, db, uid, args.dry_run)] = uid

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce the write cap inside each worker

When a user has more missing-language conversations than the remaining --max-writes budget, this worker receives no budget and commits every matching conversation before returning; the coordinator checks the limit only afterward. Thus a staged --max-writes 1000 run can perform thousands of writes for one large user, plus writes from other in-flight workers. Fresh evidence on the current head is that the bounded scheduler still calls process_user_conversations without a remaining-write limit, so the earlier post-hoc cap issue remains.

Useful? React with 👍 / 👎.

Comment on lines +169 to +170
if write_budget_exhausted:
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Drain pending results before reporting success

With --max-writes and multiple workers, reaching the limit breaks out while other futures remain pending; exiting the executor context waits for those workers, so they can still commit, but their results and exceptions are never consumed. If one of those commits fails, failures remains zero and the migration logs Done and exits successfully despite a partial backfill. Fresh evidence on the current head is this new early break, which bypasses the failure-accounting path for all remaining futures.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9170af2448

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

userDefaultsObserver = NotificationCenter.default.addObserver(
forName: UserDefaults.didChangeNotification,
object: nil,
queue: nil
queue: .main

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep UserDefaults delivery off the main queue

When a background auth commit writes UserDefaults while the main thread is waiting on the session fence, queue: .main synchronously waits for the main thread and recreates the frozen sign-in deadlock fixed by 6467d6fc8. This backend-language commit reverses that fix; retain queue: nil and asynchronously hop to the main actor instead.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment on lines +101 to +102
except Exception as e:
logger.error(f"Error processing {uid}: {e}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail when a migration worker update fails

If any Firestore stream or batch commit raises, this handler logs the exception but still reaches Done and exits successfully, allowing automation to record a partially migrated database as complete. Although the earlier thread cites 8a6c13d5e6 as the fix, that commit is not present in this reviewed tree; these catch-and-continue lines are fresh evidence that failures still need to be accumulated and produce a nonzero exit.

Useful? React with 👍 / 👎.

@@ -286,7 +286,7 @@ class CreateConversation(BaseModel):
photos: List[ConversationPhoto] = []

source: ConversationSource = ConversationSource.omi
language: Optional[str] = None
language: Optional[str] = 'en'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass the resolved sync language into the create model

For a new sync conversation whose detected or explicitly selected language is non-English, process_segment still constructs CreateConversation without language, so this default makes _get_conversation_obj persist en even though processing received the resolved language separately. The prior thread's cited fix 02fb397539 is not an ancestor of this reviewed commit; the unchanged omission at backend/utils/sync/pipeline.py:1149-1158 is fresh evidence that the resolved language must be passed into this model.

Useful? React with 👍 / 👎.

Comment thread app/lib/main.dart Outdated
}
await _init();
runApp(const MyApp());
}, (error, stack) => FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard Crashlytics until Firebase initialization succeeds

If _init() throws before a default Firebase app exists—for example because flavor configuration is missing or invalid—the zone handler calls FirebaseCrashlytics.instance, which itself requires that Firebase app and throws a second error, masking the original startup failure and losing its diagnostics. Restore the Firebase.apps.isNotEmpty guard and a non-Firebase logging fallback rather than reverting the existing startup-error fix in this unrelated backend change.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment thread backend/routers/mcp_sse.py Outdated
Comment on lines +1921 to +1925
while True:
if await request.is_disconnected():
break
yield f"event: ping\ndata: {{}}\n\n"
await asyncio.sleep(30)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Return 405 for idle MCP GET streams

Every MCP client that opens this GET now occupies a Cloud Run request slot in an infinite ping loop until disconnect or the one-hour request timeout, despite the endpoint carrying no protocol messages. This directly reverses the parent commit's production fix after roughly 1,200 concurrent parked streams exhausted container concurrency and pushed MCP tool-call latency into minutes; keep the stateless transport's 405 Method Not Allowed response instead.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines 640 to 641
}) async {
Logger.debug('initiateWebsocket in capture_provider');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Deduplicate concurrent transcription socket attempts

When the 15-second keep-alive tick fires while an earlier _initiateWebsocket call is still connecting, this implementation starts another identical attempt; the socket pool serializes but does not deduplicate them, so both can eventually open /v4/listen sessions and race the controller and UI state. Restore the per-configuration in-flight guard removed by this unrelated rollback while continuing to allow forced or differently configured attempts.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines 117 to 119
Log.d(TAG, "startService($caller): service running, forcing reconnect for $deviceAddress")
inst.forceReconnect(deviceAddress, requiresBond, caller)
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-emit device-ready for an existing Android BLE link

When Background Mode keeps the service and GATT connection alive after the Flutter engine dies, the next app launch routes its connect request to forceReconnect, which immediately returns for an already-connected peripheral and never emits Dart's only onDeviceReady signal. The app consequently waits through the connection timeout and reports the device disconnected despite the live link, disabling transcription until a manual disconnect; route this through the adopt-and-resync path restored by the reverted Android fix.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines 65 to 67
MEMORY_V3_GET_ENABLED:
value: 'false'
# Canonical graph and /v3/memories first-page cursors fail closed without this HMAC.
MEMORY_V3_CURSOR_SECRET:
secret:
name: prod-omi-backend-secrets
key: MEMORY_V3_CURSOR_SECRET
MCP_OAUTH_CLIENTS_JSON:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve the production memory cursor secret binding

On the next production Cloud Run or GKE deployment, this overlay no longer injects MEMORY_V3_CURSOR_SECRET, even though /v1/knowledge-graph and signed canonical-memory cursors fail closed when the HMAC secret is absent. Production was already repaired to consume this secret, so deploying this tree strips the live binding and makes Mind Map and cursor-backed reads return missing_cursor_secret; restore the secret and version entries in every production serving target.

AGENTS.md reference: backend/AGENTS.md:L171-L173

Useful? React with 👍 / 👎.

Comment on lines +334 to +338
final permission = await Geolocator.checkPermission();
if (permission == LocationPermission.always || permission == LocationPermission.whileInUse) {
await ForegroundUtil.initializeForegroundService();
await ForegroundUtil.startForegroundTask();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not start foreground audio from location permission

For every mobile user who has granted location permission, opening Home now starts flutter_foreground_task even when no capture is active. On iOS this holds AVAudioSession and leaves the green microphone indicator and foreground service running while idle, producing the all-day battery drain fixed by b5b77f5fa; foreground audio ownership must instead follow an active capture with recent frames.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines +342 to +345
// Proactive notifications are now OFF by default for everyone. Run the one-time
// migration before any assistant can fire, so existing users are flipped to Off
// once (they can re-enable in Settings).
NotificationService.migrateToOffByDefaultIfNeeded()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep proactive notifications at the Balanced default

This launch migration writes frequency 0 locally and to the backend for every install that has not previously run the old off-by-default migration, disabling proactive notifications for both existing users and fresh installs. It reverses the shipped Balanced-default migration, which preserves explicit opt-in levels and only enables the default-on Live Suggestions and Insight categories; call migrateToBalancedDefaultIfNeeded so a backend-language change does not silently opt the user base out again.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 9170af2 to e72b0bb Compare August 14, 2026 08:45
@undivisible

Copy link
Copy Markdown
Collaborator Author

Note: the branch briefly carried a commit (9170af2) that reverted ~1281 files of recent origin/main work plus the sync-language propagation, breaking Dart/OpenAPI/Android/Hygiene checks. That was an accidental push from a stale worktree and has been replaced by force-push back to the correct state (9-file diff, migration cursor/max-writes fixes intact, tests passing, ratchet clean). Re-running CI on the corrected head.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 669c7deebe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/models/memory_product.py Outdated
@@ -85,7 +56,7 @@ class ProductMemorySearchResponse(BaseModel):

uid: str = Field(description='Authenticated user id.')
query: str = Field(description='Search query string.')
items: List[ProductMemorySearchItem] = Field(description='Default-visible memory rows for the current page.')
items: List[MemoryItem] = Field(description='Default-visible memory items for the current page.')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the product-memory projection response type

For every populated /memory/search or /memory/archive/search response, the read seams return projection rows containing fields such as memory_id, lifecycle_status, and date, not full MemoryItem records with required fields such as version, uid, status, and timestamps. FastAPI therefore fails response validation and returns 500 whenever either search finds a result, while misleadingly allowing empty pages through; keep these two responses typed to the projection model and reserve MemoryItem for the vector route that actually emits it.

Useful? React with 👍 / 👎.

@@ -350,7 +350,6 @@ async def send_onboarding(event: Dict[str, Any]) -> None:
await request.websocket.send_json(event)

self.onboarding_handler = OnboardingHandler(request.uid, send_onboarding, self.transcripts.enqueue)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Send the initial speech-profile onboarding question

When onboarding_mode starts, this now only constructs the handler and never invokes send_current_question; there is also no remaining start_onboarding receiver path in this tree. Because answer and skip handling cannot run before question zero is delivered, both onboarding and Settings speech-profile sessions remain on an empty question screen at 0% indefinitely despite the socket being active.

Useful? React with 👍 / 👎.

Comment on lines +3157 to +3163
try await ActionItemStorage.shared.deleteActionItemByBackendId(
task.id,
deletedBy: "user",
authorization: Self.localMutationAuthorization(
snapshot: lease.authorizationSnapshot
)
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retain a tombstone until task deletion is acknowledged

When a synced task is deleted while offline or the backend DELETE fails, this call hard-deletes the only local record before the request is attempted, and the catch path merely logs the failure. A later cloud hydration consequently sees the still-present backend row with no pending tombstone and re-inserts the task, so user-deleted tasks resurrect; mark the row deleted/unsynced and retry until the backend acknowledges it instead. This stale rollback also removes the behavioral coverage for that contract.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment thread backend/routers/updates.py Outdated
@@ -916,28 +916,17 @@ async def get_desktop_appcast_xml(
async def download_latest_desktop_release(
platform: str = Query(default="macos", pattern="^(macos|windows|linux)$"),
channel: str = Query(default="stable", pattern="^(beta|stable)$"),
identity: Optional[str] = Query(default=None, pattern="^(stable|beta)$"),
identity: str = Query(default="stable", pattern="^(stable|beta)$"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Default beta-channel downloads to the beta identity

The public macos.omi.me/beta redirect calls /v2/desktop/download/latest?channel=beta without an identity parameter, so this new default selects the generic stable-identity DMG from the beta release rather than the separately installable Omi Beta artifact. Users following the public beta link therefore install the production bundle identity and production services instead of the isolated com.omi.computer-macos.beta build; when identity is omitted, derive it from the requested channel.

Useful? React with 👍 / 👎.

Comment on lines 366 to 368
func startMicrophoneAudioCapture() async {
guard let audioCaptureService = audioCaptureService else { return }

// Authorization first, capture second. CoreAudio HAL capture never triggers the
// system microphone prompt on its own: with a notDetermined or revoked TCC entry it
// "succeeds" and delivers zero samples forever. The silent-mic watchdog then reads
// those zeros as a dead device and loops the user through rebuilds into a
// "Microphone Isn't Capturing Audio" alert every ~90s — a permission problem wearing
// a hardware costume. startTranscription() has its own guard, but resume, the meeting
// gate, and the watchdog's own rebuild all arm capture through here without passing it.
var gateAction = MicrophoneCaptureAuthorizationPolicy.action(
for: AudioCaptureService.authorizationStatus())
if gateAction == .requestPermission {
log("Transcription: microphone permission undetermined — requesting before capture")
gateAction = MicrophoneCaptureAuthorizationPolicy.action(
afterRequestGranted: await AudioCaptureService.requestPermission())
}
guard gateAction == .proceed else {
surfaceMicrophonePermissionAlert()
stopTranscription()
return
}

configureSharedCaptureWatchdog(audioCaptureService)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate shared microphone capture on TCC authorization

When microphone permission is denied, revoked, or still undetermined, resume, meeting-gate, and recovery paths can enter this method without passing through the separate startTranscription permission check. CoreAudio HAL then appears to start but supplies zero samples, causing repeated capture rebuilds followed by a misleading hardware-failure alert instead of prompting for or directing the user to microphone permission; check authorization here before arming the shared capture stack. This stale rollback also removes the policy and regression tests for the non-primary entry paths.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment thread app/lib/providers/app_provider.dart Outdated
Comment on lines +304 to +310
} finally {
isSearching = false;
notifyListeners();

String? _selectedCapabilityId() {
final capability = filters['Capabilities'];
return capability is AppCapability ? capability.id : null;
if (_pendingSearchQuery != queryBeingSearched) {
performServerSearch();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep search state active while draining the latest query

When the user changes an Apps query while the previous request is in flight, the pending query is noticed here, but isSearching is first cleared and listeners are notified before the replacement request starts. The Apps page consequently renders the old or empty results as “No apps found” for at least one frame even though the latest search is still pending; filter-only changes with unchanged text are worse because the string comparison does not queue a replacement at all. Track search intent by revision and keep one draining operation in the searching state until the newest request settles.

Useful? React with 👍 / 👎.

Comment thread backend/utils/other/chat_file.py Outdated
Comment on lines +142 to +143
def upload(file_path: Union[str, Path]) -> Dict[str, Any]:
_record_direct_file_chat_surface()
_assert_direct_file_chat_allowed()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow the acknowledged direct file-chat surface

In the production runtime, OMI_LLM_GATEWAY_FEATURE_MODE is gateway while OMI_LLM_GATEWAY_ALLOW_DIRECT_MODEL_EXCEPTION is false, so this guard raises GatewayDirectModelSurfaceBlocked before every file or image upload reaches OpenAI. File chat has no gateway implementation because Files, Assistants, and vision are direct-only surfaces; record this acknowledged exception and allow it rather than making all attachment uploads fail in production.

Useful? React with 👍 / 👎.

Comment thread backend/utils/stt/vad.py Outdated
@@ -218,7 +160,7 @@ def vad_is_empty(
try:
with open(file_path, 'rb') as file:
files = {'file': (file_path.split('/')[-1], file, 'audio/wav')}
response = requests.post(hosted_vad_url, files=files, timeout=_hosted_vad_timeout_seconds())
response = requests.post(hosted_vad_url, files=files, timeout=300)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound hosted VAD deadlines before local fallback

When HOSTED_VAD_API_URL accepts a connection but stalls, each synchronous conversation-processing call now occupies its worker for as long as five minutes before the existing local ONNX fallback can run. A hosted VAD degradation can therefore leave processing cards hanging and exhaust the bounded worker pool across otherwise healthy requests; use separate short connect/read deadlines, with the read deadline capped well below this value, so the fallback remains operational.

Useful? React with 👍 / 👎.

@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 669c7de to 47c6bba Compare August 14, 2026 09:04
@undivisible

Copy link
Copy Markdown
Collaborator Author

Rebuilt the branch cleanly on current origin/main: the previous head carried stale-worktree commits that reverted ~1281 files of main's work and dropped the sync-language propagation. The current head is a clean 9-file diff (models default, sync language preservation, resumable migration, tests, generated Dart/OpenAPI) with all 81 focused tests passing and the line-count ratchet clean. CI re-running.

@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_81c55c48-a21f-408d-a47e-0ac1ede0e47a)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bdf321d264

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 63 to 65
if let continuation = handle.continuation {
handle.continuation = nil
continuation.resume(returning: .cancelled)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Serialize cancellation with the gate handoff

When a queued chat-context task is cancelled after release() takes the slot continuation but before it resumes it, this cancellation handler can still read the same continuation from handle.continuation; it resumes .cancelled, and release() then resumes .granted, causing a checked-continuation double-resume trap that can crash desktop chat. Keep a single synchronized continuation owner across cancellation and grant handoff.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

return firestore.Client(credentials=credentials, project=project_id)

prepare_google_credentials()
return firestore.Client()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pin Firestore to the customer service-account project

In the checked backend-listen dev deployment, SERVICE_ACCOUNT_JSON identifies the customer project while GOOGLE_CLOUD_PROJECT is explicitly based-hardware-dev; constructing firestore.Client() without the service account's explicit project_id therefore selects the compute project and makes user, subscription, and usage reads hit shadow or missing documents. Restore the explicit credentials/project binding instead of relying on default project resolution.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines +51 to +53
if (
aseg.duration_seconds < 10
): # TODO: validate duration more accurately, segment.last.end - segment.first.start - 10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate uploaded audio against the transcript span

When an upload is longer than ten seconds but substantially shorter than its conversation transcript—for example, 14 seconds of audio for segments spanning 25 seconds—this fixed threshold accepts the truncated file and starts post-processing it as complete. That produces post-processing output from incomplete audio rather than cancelling the bad upload; retain the transcript-derived minimum-duration check.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines 241 to 243
} finally {
if (succeeded && options?.protectPinnedBindingAfterWork) {
this.protectPinnedBinding(worker.idlePinnedBindingId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recycle pi-mono workers before draining queued sends

When a pi-mono execution throws after dispatch, this finally block immediately drains queued leases without marking the process-local worker unhealthy, removing it from the pool, or invalidating its pinned binding. The next chat send can therefore reacquire the same poisoned worker and fail repeatedly until the agent daemon is restarted; recycle the failed worker before making queued sends runnable.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment on lines +518 to +524
conversations = _filterPendingDeletes(result.items);

// processing convos
processingConversations = conversations.where((m) => m.status == ConversationStatus.processing).toList();

// completed convos
conversations = conversations.where((m) => m.status == ConversationStatus.completed).toList();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve websocket completions across stale list refreshes

When a conversation completes over the websocket while fetchConversations() is awaiting its list request, a stale response that still labels the row as processing reaches these assignments afterward: it recreates the Processing card and filters the live completed row out of conversations. The user then sees a completed conversation revert to Processing until a later authoritative refresh; reconcile the response against websocket revisions instead of replacing both lists wholesale.

AGENTS.md reference: AGENTS.md:L50-L52

Useful? React with 👍 / 👎.

Comment on lines 678 to 681
// Cheap early exits before resolving the active window.
let idleSeconds = systemIdleSeconds()
if idleSeconds >= captureTrigger.idleThreshold {
logCaptureGate("idle")
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Exempt active media playback from the HID idle gate

When a user watches media without keyboard or mouse input past the idle threshold, browsers and video players keep the display awake but systemIdleSeconds() still exceeds the threshold, so this return stops all proactive captures and nudges for the remainder of playback. Restore the power-management assertion check so active media viewing is treated as presence rather than absence.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment on lines 281 to 284
guard let window else { return }
attachedWindow = window
reassertIfNeeded(force: true)
// The transparent shell must pass clicks on its dead margins through to whatever is behind
// the window — see `ShellClickThrough.swift`.
mouseInterceptionSync = ShellMouseInterceptionSync(window: window)
updateObserver = NotificationCenter.default.addObserver(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Pass clicks through transparent shell margins

The shell NSWindow is larger than the visible glass panels it contains, but attaching it here no longer installs any window-level mouse-interception sync. AppKit therefore treats transparent title-band, margin, and inter-panel space as part of Omi's window, swallowing clicks intended for applications underneath and preventing those apps from activating; toggle ignoresMouseEvents over non-content regions while retaining interactive resize and modal regions.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Comment on lines +454 to +458
self.isOpaque = false
self.backgroundColor = .clear
self.hasShadow = false
// NSPanel defaults hidesOnDeactivate to true, which orders the notch out
// when another app activates. isFloatingPanel is the overlay companion;
// re-assert always-on-top after it so AppKit cannot drop us to .floating.
self.isFloatingPanel = true
self.hidesOnDeactivate = false
self.level = Self.alwaysOnTopLevel
self.collectionBehavior = Self.overlayCollectionBehavior
self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the floating panel visible when Omi deactivates

After the floating bar is shown, activating another application deactivates this NSPanel; without overriding the panel default hidesOnDeactivate behavior, AppKit orders the notch/bar out, and the removed durable-visibility recovery means it may not return until the user invokes Push-to-Talk. Configure it as a non-hiding floating overlay and reassert that presentation contract when ordering it forward.

AGENTS.md reference: desktop/macos/AGENTS.md:L356-L359

Useful? React with 👍 / 👎.

Rebuild of PR branch on current origin/main. The prior head carried an
accidental revert commit reverting ~1281 files of main; this branch
contains only the intended feature diff (9 files).

Defaults Conversation, CreateConversation, and
ExternalIntegrationCreateConversation language to 'en', preserving
explicit non-English values; sync pipeline threads the detected/preferred
language into CreateConversation; resumable bounded backfill migration
(008) pages users, supports --max-users/--max-writes/--start-after/
--dry-run/--workers, and exits non-zero on per-user failures. Tests: 81
passed.
@undivisible
undivisible force-pushed the fix/migrate-db-language-field-10040660832344661720 branch from 2a0977e to d459537 Compare August 14, 2026 12:41
@cursor

cursor Bot commented Aug 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_366677c4-376d-4daa-b76f-8e9d0d44b869)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d459537faa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +187 to +189
logger.info(
'Results: %d conversations %s across %d users (%d writes).', total_updates, verb, users_processed, total_writes
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Log the cursor needed to continue staged runs

After a successful --max-users stage, the only output is aggregate counts, even though the documented resume procedure requires the last processed UID. Re-running the same staged command without a usable --start-after value just processes the same first users again, so operators cannot advance through the migration using the documented workflow; emit a correctly ordered resume cursor in the success summary.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI backend Backend Task (python) needs-maintainer-review Needs a human maintainer to sign off before merge privacy-review Touches user-data persistence, permissions, or privacy-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants